#!/usr/bin/python3
# -*- coding: utf-8 -*-
# @Time    : 2021/10/19
# @Author  : ZhangAiqiang
# @FileName: devTest.py
import socket
import operator
from time import sleep
import struct
from threading import Thread
import functools, signal
def timeout(timeout):
    def deco(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            res = [TimeoutError('function [%s] timeout [%s seconds] exceeded!' % (func.__name__, timeout))]
            def newFunc():
                try:
                    res[0] = func(*args, **kwargs)
                except Exception as e:
                    res[0] = e
            t = Thread(target=newFunc)
            t.daemon = True
            try:
                t.start()
                t.join(timeout)
            except Exception as je:
                print ('error starting thread')
                raise je
            ret = res[0]
            if isinstance(ret, BaseException):
                raise ret
            return ret
        return wrapper
    return deco
# def timeout(sec):
# 	"""
# 	timeout decorator
# 	:param sec: function raise TimeoutError after ? seconds
# 	"""
# 	def decorator(func):
# 		@functools.wraps(func)
# 		def wrapped_func(*args, **kwargs):

# 			def _handle_timeout(signum, frame):
# 				err_msg = f'Function {func.__name__} timed out after {sec} seconds'
# 				raise TimeoutError(err_msg)

# 			signal.signal(signal.SIGALRM, _handle_timeout)
# 			signal.alarm(sec)
# 			try:
# 				result = func(*args, **kwargs)
# 			finally:
# 				signal.alarm(0)
# 			return result
# 		return wrapped_func
# 	return decorator

HOST = '192.168.0.58'
PORT = 4001
class HVMsg:
	def __init__(self, host, port, commands):
		self.host = host
		self.port = port
		self.commands = commands
		self.conn = None
		print('初始化Host:Port={}:{}'.format(self.host,self.port))
	def __enter__(self):
		self.conn = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
		print('连接Host:Port={}:{}'.format(self.host,self.port))
		return self
	def __exit__(self, exc_type, exc_value, exc_traceback):
		# print('[__exit__ {}]: '.format('HV'))
		# exc_traceback为None，代表没有异常
		if exc_traceback is None:
			self.conn.close()
			# print('正常退出')
		else:
			print('自动关闭所有高压')
			# self.closeAll()
			self.conn.close()
	def connect(self):
		if self.conn==None:
			self.conn = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
			print('连接Host:Port={}:{}'.format(self.host,self.port))
		self.conn.connect((self.host, self.port))
	def queryCh(self, ch):
		return 0
	def openCh(self, ch, master=True):
		return 0
	def closeCh(self, ch):
		return 0
	@timeout(5)
	def openAll(self, mRunning=True, sRunning=False, output=True):
		print('全路控制')
		if mRunning:
			self.sendCmd(0xAF, [0x01, 0x40], False, 2)
		if sRunning:
			self.sendCmd(0xAF, [0x01, 0x04], False, 2)
		return
	@timeout(3)
	def closeAll(self):
		self.sendCmd(0xFF, [], False, 0.5)

	def printStrAsHexArray(self, s):
		hexStrArray = []
		for c in s:
			tmp = hex(ord(c))
			if len(tmp) == 3:
				tmp = '0x0' + tmp[-1]
			tmp = '0x' + tmp[-2:].upper()
			hexStrArray.append(tmp)
		hexLine = ''
		for hexStr in hexStrArray:
			hexLine = hexLine + hexStr + ', '
		print(hexLine)

	def calcXor(self, intArray):
		ret = 0
		for b in intArray:
			ret = operator.xor(ret, b)
		# return ret.to_bytes(1,byteorder='little')
		# return format(ret,'x')
		return ret

	def sendCmd(self, cmd, cmdBytes, needRecv, waitTimeAfterSend=None):
		# 构建命令Byte
		fullCmdBytes = [0xAA, 0xCC, cmd, 0x00]
		for b in cmdBytes:
			fullCmdBytes.append(b)
		# xor = self.calcXor([int(by,16) for by in fullCmdBytes])
		xor = self.calcXor(fullCmdBytes)
		fullCmdBytes.append(xor)
		fullCmdBytes.append(0x0D)
		fullCmdBytes.append(0x0A)
		# 构建命令字符串
		toSendStr = b''
		for by in fullCmdBytes:
			# toSendStr += bytes.fromhex(by)
			toSendStr += by.to_bytes(1,byteorder='little')
		# self.printStrAsHexArray(toSendStr)
		# print(toSendStr)
		self.conn.send(toSendStr)
		if waitTimeAfterSend != None and waitTimeAfterSend > 0:
			sleep(waitTimeAfterSend)
		if needRecv:
			# print('Begin receive')
			ret = self.conn.recv(1024)
			# print('recv len: {0}'.format(len(ret)))
			# print(ret)
			# self.printStrAsHexArray(ret)	
			return ret

	def sendEmptyCmd(self):
		# 0xCC 空指令
		print('空指令')
		self.sendCmd(0xCC, [], True)	

	def sendHexRequest(self):
		# HEX回传
		print('HEX回传')
		self.sendCmd(0xCB, [0xAA], False, 0.5)
	def sendTextRequest(self):
		# 文本回传
		# print('文本回传')
		self.sendCmd(0xCB, [0x55], False, 0.5)
	@timeout(3)
	def sendQueryChannelCmd(self, chNo):
		# 查询单路数据
		print('查询单路数据')
		reply = self.sendCmd(0xCE, [chNo], True, 0.5)
		tmp = struct.unpack('HHffc', reply[:-2])
		stat = '{:0>16b}'.format(tmp[1])
		faultcode, output, m_err, m_running, red_led, green_led, s_err, s_running,red_led1,green_led1 = int(stat[-11:-9],2),int(stat[-9],2),int(stat[-8],2),int(stat[-7],2),int(stat[-6],2),int(stat[-5],2),int(stat[-4],2),int(stat[-3],2),int(stat[-2],2),int(stat[-1],2)
		# print('ch: {2}, volt: {0}, current: {1}'.format(volt, current, chNo))
		return [faultcode, output, m_err, m_running, red_led, green_led, s_err, s_running,red_led1,green_led1, tmp[2],tmp[3]]
	@timeout(3)
	def sendQueryAllChannelCmd(self):
		# 查询全部数据
		print('查询全部数据')
		reply = self.sendCmd(0xCF, [], True)
		channelInfo = []
		for i in range(25):
			tmp = struct.unpack('HHffc',reply[(i*15):(i*15+13)])
			stat = '{:0>16b}'.format(tmp[1])
			# print(stat)
			assert int(stat[:5],2)==i
			faultcode, output, m_err, m_running, red_led, green_led, s_err, s_running,red_led1,green_led1 = int(stat[-11:-9],2),int(stat[-9],2),int(stat[-8],2),int(stat[-7],2),int(stat[-6],2),int(stat[-5],2),int(stat[-4],2),int(stat[-3],2),int(stat[-2],2),int(stat[-1],2)
			channelInfo.append([faultcode, output, m_err, m_running, red_led, green_led, s_err, s_running,red_led1,green_led1, tmp[2],tmp[3]])
			# print(channelInfo[i])
		return channelInfo

	def sendControlChannelCmd(self, chNo, mRunning=True, sRunning=False, output=True):
		# 单路控制
		print('单路控制')
		highByte = chNo << 3
		if output:
			highByte += 1<<0
		lowByte = 0
		if mRunning:
			lowByte += 1<<6
		if sRunning:
			lowByte += 1<<2
		return self.sendCmd(0xCD, [highByte, lowByte], False, 0.5)
	@timeout(3)
	def sendCaseStatusQueryCmd(self):
		# 查询温度时间转速
		# print('查询温度时间转速')
		reply = self.sendCmd(0xBB, [], True, 0.5)
		fans_rpm = [0,0,0,0]
		temper, fans_rpm[0],fans_rpm[1],fans_rpm[2],fans_rpm[3], present_time, accumulated_time, _, state = struct.unpack('f4HIIBc',reply[2:-2])
		# print('temper: {:.2f}, fans_rpm: {:.2f},{:.2f},{:.2f},{:.2f}'.format(temper,fans_rpm[0],fans_rpm[1],fans_rpm[2],fans_rpm[3]))
		return temper,fans_rpm,present_time, accumulated_time
if __name__=='__main__':
	hv = HVMsg(HOST,PORT,'')
	# s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
	# s.connect((HOST, PORT))
	hv.connect()
	'''
	sendEmptyCmd(s)
	sendHexRequest(s)
	sendControlChannelCmd(s, 1, False, True, True)
	#for no in range(13, 25):
	#	sendQueryChannelCmd(s, no) 
	sendQueryChannelCmd(s, 12)
	sendCaseStatusQueryCmd(s)
	sendQueryAllChannelCmd(s)

	s.close()
	'''
	hv.sendEmptyCmd()
	hv.sendHexRequest()
	# hv.sendControlChannelCmd(1, False, True, True) 
	hv.sendQueryChannelCmd(12)
	hv.sendCaseStatusQueryCmd()
	hv.sendQueryAllChannelCmd()
	hv.sendTextRequest()